package com.vhall.component.service.account.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.vhall.component.dao.account.*;
import com.vhall.component.dao.rbac.RbacRoleMenuesMapper;
import com.vhall.component.entity.account.*;
import com.vhall.component.entity.account.dto.ExchangeSessionIdDTO;
import com.vhall.component.entity.account.dto.WatchUserInfoDTO;
import com.vhall.component.entity.account.enums.RoleNameEnum;
import com.vhall.component.entity.account.vo.*;
import com.vhall.component.entity.dictionary.enums.GuoXinAccountTypeEnum;
import com.vhall.component.entity.export.dto.AdminExportCommonQueryCondition;
import com.vhall.component.entity.rbac.vo.RolePermissionConsoleRequestVO;
import com.vhall.component.entity.rbac.vo.RolePermissionConsoleResponseVO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.vo.RoomSupplyEntityDTO;
import com.vhall.component.framework.common.constant.CommonConstant;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.AccountConstant;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.constant.RoomConstant;
import com.vhall.component.plugin.common.enums.AccountType;
import com.vhall.component.plugin.common.enums.DeleteStatusEnum;
import com.vhall.component.plugin.common.enums.LoginTypeEnum;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.common.support.VssToken;
import com.vhall.component.plugin.common.support.VssTokenThreadLocal;
import com.vhall.component.plugin.common.utils.BeanCopyUtil;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.common.utils.RandomUtil;
import com.vhall.component.plugin.common.utils.RedisLocker;
import com.vhall.component.plugin.common.vo.MultipleRoleVO;
import com.vhall.component.plugin.core.enums.SendMessageTypeEnum;
import com.vhall.component.plugin.core.service.GuoXinService;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.plugin.core.service.SsoService;
import com.vhall.component.service.account.AccountService;
import com.vhall.component.service.account.CodeService;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.watchlimit.WatchLimitInnerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户表服务实现类
 *
 * @author xuyongzheng
 * @date 2021-04-06 19:01:15
 */
@Service
@Slf4j
public class AccountServiceImpl extends ServiceImpl<AccountMapper, AccountEntity> implements AccountService {

    private static final String PHONE = "phone";
    private static final String LIMIT_1 = "limit 1;";
    private static final String NICKNAME = "nickname";
    private static final String VISITOR = "visitor";
    private static final String ACCOUNT_TYPE = "account_type";


    @Autowired
    private AccountMapper accountMapper;

    @Value("${vhall.account.default-avatar}")
    private String defaultAvatar;

    @Value("${vhall.sms.fake:false}")
    private Boolean fakeSms;

    @Value("${vhall.anchor.login-check}")
    private boolean checkAnchor;
    /**
     * 是否开启房间审核
     * 1为开启
     * 0为关闭房间审核
     */
    @Value("${project.room.audit:0}")
    private Integer roomAuditWitch;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplateStr;

    @Autowired
    private PaasService paasService;

    @Autowired
    private CodeService codeService;

    @Autowired
    private WatchLimitInnerService watchLimitInnerService;


    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private NicknamesMapper nicknamesMapper;

    @Resource
    private SsoService ssoService;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private RbacRoleMenuesMapper rbacRoleMenuesMapper;
    @Resource
    private AdminMenuMapper menuMapper;
    @Resource
    private OrgMapper orgMapper;
    @Resource
    private GuoXinService guoXinService;

    @Override
    public AccountEntity add(String phone, String username, String nickname, int sex, int type) {

        QueryWrapper<AccountEntity> where = new QueryWrapper<>();
        where.eq(PHONE, phone);
        where.eq(ACCOUNT_TYPE, type);
        where.last(LIMIT_1);
        AccountEntity entity = accountMapper.selectOne(where);
        if (null != entity) {
            throw new BusinessException(BizErrorCode.AUTH_PHONE_ALREADY_EXIST);
        }

        entity = new AccountEntity();
        entity.setPhone(Long.valueOf(phone));
        entity.setUsername(username);
        entity.setNickname(nickname);
        entity.setSex(sex);
        entity.setAccountType(type);
        accountMapper.insert(entity);
        return entity;
    }

    @Override
    public List<AccountVO> listByIds(String ids) {

        if (StringUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<AccountVO> accountVos = new ArrayList<>();
        List<Integer> aids = Arrays.stream(ids.split(",")).map(Integer::parseInt).collect(Collectors.toList());
        List<AccountEntity> accountEntitys = accountMapper.selectBatchIds(aids);
        for (AccountEntity accountEntity : accountEntitys) {
            AccountVO accountVO = new AccountVO();
            BeanUtils.copyProperties(accountEntity, accountVO);
            accountVos.add(accountVO);
        }

        return accountVos;

    }

    @Override
    public List<AccountVO> listByIds(List<Integer> ids) {

        if (CollectionUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<AccountVO> accountVos = new ArrayList<>();
        List<AccountEntity> accountEntitys = accountMapper.selectBatchIds(ids);
        for (AccountEntity accountEntity : accountEntitys) {
            AccountVO accountVO = new AccountVO();
            BeanUtils.copyProperties(accountEntity, accountVO);
            accountVos.add(accountVO);
        }

        return accountVos;

    }

    /**
     * 交互时序图：http://assets.processon.com/chart_image/636c62baf346fb2b79650b6b.png
     *
     * @param authLoginVO 登录参数
     * @param request     请求
     * @param response    响应
     * @param isCheckCode 是否校验短线验证码
     * @return
     * @throws BusinessException
     */
    @Override
    public AuthLoginRspVO authLogin(AuthLoginVO authLoginVO,
                                    HttpServletRequest request,
                                    HttpServletResponse response,
                                    boolean isCheckCode) throws BusinessException {
        return authLogin(authLoginVO, request, response, isCheckCode, "");
    }

    @Override
    public AuthLoginRspVO authLogin(AuthLoginVO authLoginVO,
                                    HttpServletRequest request,
                                    HttpServletResponse response,
                                    boolean isCheckCode, String name) throws BusinessException {
        //这个方法 里面 是以下登录方式 共用  改的时候 务必小心   游客登录不在这里
        // v4/account/console/auth/login      主持人端登录       login_type=1
        // v4/account/api/auth/assistant  嘉宾助理 口令登录       login_type=3
        // /v4/account/white-login    白名单 用户登录             login_type=5
        // /v4/account/api/auth/login-watch  正常观众登录         login_type=6
        // /v4/account/api/auth/login-watch  报名的观众登录    注意 这里面 有个报名  报名的时候 也走 这个观众登录   login_type=7

        Integer loginType = authLoginVO.getLoginType();
        Integer code = authLoginVO.getCode();
        String phone = authLoginVO.getPhone();

        // 1、主持人 2、观众 3、游客
        Integer accountType = authLoginVO.getType();

        //  对于登录来说  这个字段dd   roleName 只是用来 方便前端标识  这个人是 嘉宾 还是 助理
        //  roleName=3  代表 嘉宾  roleName=4  代表助理
        Integer roleName = null;
        if (accountType == null) {
            accountType = AccountConstant.ACCOUNT_TYPE_WATCH;
        }

        String avatar = authLoginVO.getAvatar();
        if (StringUtils.isEmpty(avatar)) {
            avatar = defaultAvatar;
        }

            if (isCheckCode && !fakeSms) {
                if (!codeService.checkCode(phone, code)) {
                    throw new BusinessException(BizErrorCode.AUTH_VERIFICATION_CODE_ERROR);
                }
            }


        //白名单检测
        //嘉宾助理登录以及白名单登录时 roomId是 必传字段
        if (loginType == null) {
            loginType = LoginTypeEnum.DEFAULT.getValue();
        }
        if (loginType.equals(LoginTypeEnum.GUEST_ASSISTANT.getValue()) || loginType.equals(LoginTypeEnum.WHITE_LIST.getValue())) {
            // 登录校验
            roleName = loginVerification(authLoginVO, name);
        }
        if (StringUtils.isEmpty(name)) {
            name = phone;
        }

        //3：角色类型助理
        if (roleName != null && roleName == 3) {
            //4代表助理
            accountType = AccountConstant.ACCOUNT_TYPE_ASSIST;
        }
        //4：角色类型嘉宾
        if (roleName != null && roleName == 4) {
            //5代表嘉宾
            accountType = AccountConstant.ACCOUNT_TYPE_GUEST;
        }

        // 登录逻辑处理
        AccountDTO accountDTO = doLogin(authLoginVO, name, avatar, accountType, request, response);
        validateAccountDto(accountDTO);

        if (roleName != null) {
            //roleName=3  代表 嘉宾  roleName=4  代表助理
            accountDTO.setAType(roleName);
        }
        // 设置用户缓存
        accountDTO.setAppId(paasService.getAppId());
        accountDTO.setModules(AccountConstant.ALLOW_MODULES);
        accountDTO.setThirdPartyUserId(accountDTO.getThirdPartyUserId());
        String cacheValue = JSON.toJSONString(accountDTO);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + accountDTO.getToken(), cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);

        // 格式化返回数据
        return formatAuthLoginData(roleName, accountDTO);
    }

    private void validateAccountDto(AccountDTO accountDTO) {
        if (accountDTO == null) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        if (accountDTO.getStatus().equals(AccountConstant.STATUS_DISABLED)) {
            throw new BusinessException(BizErrorCode.AUTH_LOGIN_ACCOUNT_DISABLE);
        }
    }

    /**
     * 登录校验
     */
    private Integer loginVerification(AuthLoginVO authLoginVO, String name) {
        Integer roleName = null;
        if (StringUtils.isBlank(authLoginVO.getRoomId())) {
            throw new BusinessException(BizErrorCode.AUTH_ROOMID_CODE_ERROR);
        }
        if (StringUtils.isBlank(authLoginVO.getPassword())) {
            throw new BusinessException(BizErrorCode.AUTH_PWD_CODE_ERROR);
        }
        if (StringUtils.isBlank(authLoginVO.getAssistant())) {
            //白名单校验
            watchLimitInnerService.checkWhiteAccount(authLoginVO.getRoomId(), authLoginVO.getPassword(), name);
        } else if (StringUtils.isNotEmpty(authLoginVO.getPassword())) {
            //嘉宾助理登录：口令检测
            RoomSupplyEntityDTO supply = roomInnerService.getRoomSupplyByRoomId(authLoginVO.getRoomId());
            if (supply != null) {
                if (authLoginVO.getPassword().equals(supply.getAssistantSign())) {
                    // 助理
                    roleName = Integer.parseInt(RoomConstant.ASSISTANT);
                } else if (authLoginVO.getPassword().equals(supply.getInteractionSign())) {
                    //互动口令
                    roleName = Integer.parseInt(RoomConstant.INTERACTION);
                } else {
                    throw new BusinessException(BizErrorCode.AUTH_ROLE_PASSWORD_ERROR);
                }
            }
        }
        return roleName;
    }

    /**
     * 格式化返回数据
     *
     * @param roleName   roleName
     * @param accountDTO 用户账户信息
     * @return 填充后的登录信息
     */
    @NotNull
    private AuthLoginRspVO formatAuthLoginData(Integer roleName, AccountDTO accountDTO) {
        AuthLoginRspVO authLoginRspVO = new AuthLoginRspVO();
        BeanUtils.copyProperties(accountDTO, authLoginRspVO);
        authLoginRspVO.setAppId(paasService.getAppId());
        authLoginRspVO.setPhone(accountDTO.getPhone());
        authLoginRspVO.setSaveUserUrl("");
        if (roleName != null) {
//        设置房间审核状态
            authLoginRspVO.setRoomAuditWitch(roomAuditWitch);
            authLoginRspVO.setRoleName(roleName);
        }else{
            roleName = RoleNameEnum.AUDIENCE.getValue();
        }
            accountDTO.setRoleName(roleName);
        return authLoginRspVO;
    }

    @Override
    public AuthLoginRspVO loginAnchor(AnchorLoginVO vo, HttpServletRequest request, HttpServletResponse response) {
        Integer ilId = vo.getIlId();
        if (Objects.isNull(ilId)) {
            throw new BusinessException(BizErrorCode.AUTH_ROOMID_CODE_ERROR);
        }
        String password = vo.getPassword();
        if (StringUtils.isBlank(password)) {
            throw new BusinessException(BizErrorCode.AUTH_PWD_CODE_ERROR);
        }
        RoomSupplyEntityDTO supply = checkRoomSupplyInfo(ilId, password);
        AccountDTO accountDTO = doLoginAnchor(supply.getAccountId());
        if (accountDTO.getStatus().equals(AccountConstant.STATUS_DISABLED)) {
            throw new BusinessException(BizErrorCode.AUTH_LOGIN_ACCOUNT_DISABLE);
        }
        // 设置用户缓存
        accountDTO.setAppId(paasService.getAppId());
        accountDTO.setModules(AccountConstant.ALLOW_MODULES);
        String cacheValue = JSON.toJSONString(accountDTO);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + accountDTO.getToken(),
                cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);
        return formatAuthLoginData(null, accountDTO);
    }
    @Override
    public AuthLoginRspVO loginWatchSso(WatchLoginSsoVO vo, HttpServletRequest request, HttpServletResponse response) {
//        1. 获取三方用户信息
        WatchUserInfoDTO thirdUserInfoWatch = ssoService.getThirdUserInfoWatch(vo);
//        2. 获取
        AccountEntity watchAccount = getOrCreateWatchAccountEntity(thirdUserInfoWatch);
        accountLoginUpdate(thirdUserInfoWatch,watchAccount);
        AccountDTO accountDTO = new AccountDTO();
        BeanUtils.copyProperties(watchAccount, accountDTO);
//         3. 设置用户缓存
        accountDTO.setCustId(thirdUserInfoWatch.getCustId());
        accountDTO.setFundId(thirdUserInfoWatch.getFundId());
        accountDTO.setAppId(paasService.getAppId());
        accountDTO.setModules(AccountConstant.ALLOW_MODULES);
        accountDTO.setThirdPartyUserId(watchAccount.getThirdUserId());
        accountDTO.setStaff(thirdUserInfoWatch.getStaff());
        accountDTO.setChannel(vo.getChannel());
        Integer guoXinAccountType = getGuoXinAccountType(thirdUserInfoWatch);
        accountDTO.setGuoXinAccountType(guoXinAccountType);

        Integer roleName = null;
//        4.格式化返回数据
        AuthLoginRspVO authLoginRspVO = formatAuthLoginData(roleName, accountDTO);
        authLoginRspVO.setOpenid(thirdUserInfoWatch.getOpenid());
        String cacheValue = JSON.toJSONString(accountDTO);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + accountDTO.getToken(), cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);

        authLoginRspVO.setIslogin(thirdUserInfoWatch.getIslogin());
        return authLoginRspVO;
    }

    @Override
    public JSONObject exchangeSessionId(ExchangeSessionIdDTO dto) {
        return guoXinService.exchangeSessionId(dto);
    }



    /**
     * 获取登录用户的类型
     * - 通过资金账户判定是否为交易账户
     * @param thirdUserInfoWatch
     * @return
     */
    private Integer getGuoXinAccountType(WatchUserInfoDTO thirdUserInfoWatch) {
        String fundId = thirdUserInfoWatch.getFundId();
        String custId = thirdUserInfoWatch.getCustId();
        if (StrUtil.isNotBlank(fundId)) {
            return GuoXinAccountTypeEnum.TRANSACTION_LOGIN.getValue();
        }else if(StrUtil.isNotBlank(custId)){
            return GuoXinAccountTypeEnum.BIND_ACCOUNT_USERS.getValue();
        }
        return GuoXinAccountTypeEnum.REGISTERED_USERS.getValue();
    }

    /**
     * 获取三方用户是否为总部人员
     * @param userCode
     * @param userName
     * @return
     */
    private Boolean getHeadquartersMarkerFlag(String userCode,String userName) {
        return "S000".equals(userCode);
    }

    @NotNull
    private AccountEntity getOrCreateWatchAccountEntity(WatchUserInfoDTO thirdUserInfoWatch) {
        //        2. 判定用户是否登录过系统
        String userCode = thirdUserInfoWatch.getUserCode();
        String mobile = thirdUserInfoWatch.getMobile();
        String name = thirdUserInfoWatch.getName();
        String custId = thirdUserInfoWatch.getCustId();
        LambdaQueryWrapper<AccountEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(AccountEntity::getAccountType, AccountType.WATCHER.getCode())
                .eq(AccountEntity::getThirdUserId,userCode)
                .last(" limit 1");
        AccountEntity watchAccount = accountMapper.selectOne(queryWrapper);
//        3. 创建用户/更新用户
        if (Objects.isNull(watchAccount)) {
            watchAccount = new AccountEntity();
            watchAccount.setAccountType(AccountType.WATCHER.getCode());
            watchAccount.setPhone(Long.parseLong(mobile));
            watchAccount.setSex(AccountConstant.SEX_MEN);
            watchAccount.setCustid(custId);
            watchAccount.setThirdUserId(userCode);
            watchAccount.setNickname(name);
            watchAccount.setUsername(name);
            LocalDateTime now = LocalDateTime.now();
            watchAccount.setUpdatedAt(now);
            watchAccount.setCreatedAt(now);
            accountMapper.insert(watchAccount);
        }
        return watchAccount;
    }

    private void accountLoginUpdate(WatchUserInfoDTO thirdUserInfoDTO, AccountEntity accountEntity) {
        String userId = thirdUserInfoDTO.getUserCode();
        String name = thirdUserInfoDTO.getName();
        String phone = thirdUserInfoDTO.getMobile();
        String orgCode = thirdUserInfoDTO.getOrgCode();
        String custId = thirdUserInfoDTO.getCustId();
        // 生成token
        String token = createLoginToken(userId);
        //            同步和 UBS 用户的信息
        accountEntity.setPhone(Long.parseLong(phone));
        accountEntity.setCustid(custId);
        accountEntity.setUsername(name);
        accountEntity.setNickname(name);
        // 更新token, 同时删除老的token
        accountEntity.setToken(token);
        accountEntity.setUpdatedAt(LocalDateTime.now());
        accountEntity.setOrgCode(orgCode);
        accountMapper.updateById(accountEntity);
    }
    /**
     * 校验房间的密码
     *
     * @param ilId     房间id
     * @param password 密码
     * @return 房间关联信息
     */
    @NotNull
    private RoomSupplyEntityDTO checkRoomSupplyInfo(Integer ilId, String password) {
        RoomSupplyEntityDTO supply = roomInnerService.getRoomSupplyByIlId(ilId);
        if (supply != null) {
            String anchorSign = supply.getAnchorSign();
            if (!password.equals(anchorSign)) {
                throw new BusinessException(BizErrorCode.AUTH_ROLE_PASSWORD_ERROR);
            }
        } else {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        return supply;
    }

    /**
     * 执行主播登录功能
     * 当前版本主播登录完全等价于主持人登录,直接使用房间主持人账户进行登录
     *
     * @param accountId 账户id
     * @return 登录信息
     */
    private AccountDTO doLoginAnchor(Integer accountId) {
        // 更新用户信息
        LambdaQueryWrapper<AccountEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(AccountEntity::getAccountId, accountId)
                .last(LIMIT_1);
        AccountEntity accountEntity = accountMapper.selectOne(wrapper);
        if (Objects.nonNull(accountEntity)) {
            // 生成token
            String token = createLoginToken(accountEntity.getPhone().toString());
            // 更新token, 同时删除老的token
            accountEntity.setToken(token);
            accountEntity.setUpdatedAt(LocalDateTime.now());
            accountMapper.updateById(accountEntity);
        } else {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        AccountDTO accountDTO = new AccountDTO();
        BeanUtils.copyProperties(accountEntity, accountDTO);
        return accountDTO;
    }

    @Override
    public AccountDTO doLogin(AuthLoginVO authLoginVO, String name, String avatar, Integer accountType,
                              HttpServletRequest request, HttpServletResponse response) {

        String phone = authLoginVO.getPhone();
        String nickname = authLoginVO.getNickname();
        String visitorId = authLoginVO.getVisitorId();
        String deviceId = authLoginVO.getDeviceId();

        // 生成token
        String token = createLoginToken(phone);
        // 更新用户信息
        LambdaQueryWrapper<AccountEntity> wrapper = new LambdaQueryWrapper<AccountEntity>()
                .eq(AccountEntity::getPhone, phone)
                .eq(AccountEntity::getAccountType, accountType).last(LIMIT_1);
        AccountEntity accountEntity = accountMapper.selectOne(wrapper);

        if (accountEntity == null && StringUtils.isNotEmpty(visitorId)) {
            try {
                Integer accountId = Integer.parseInt(visitorId);
                accountEntity = accountMapper.selectById(accountId);
                //判断当前visitor_id的用户是否为游客用户，如果是，将用户类型设置为观看端类型，
                // 如果不是，避免前端误传，更新正常用户数据，则将实例还原为null
                if (accountEntity.getAccountType() == AccountConstant.ACCOUNT_TYPE_VISITOR) {
                    accountEntity.setAccountType(AccountConstant.ACCOUNT_TYPE_WATCH);
                    accountEntity.setPhone(Long.valueOf(phone));
                    accountEntity.setUsername(name);
                    accountEntity.setNickname(nickname);
                } else {
                    accountEntity = null;
                }
            } catch (Exception e) {
                accountEntity = null;
            }
        }
        if (accountEntity != null) {
            // 更新token, 同时删除老的token
            accountEntity.setToken(token);
            accountEntity.setLogintokenEncrypted(authLoginVO.getLogintokenEncrypted());
            accountEntity.setUpdatedAt(LocalDateTime.now());
            accountEntity.setNickname(nickname);
            accountMapper.updateById(accountEntity);
        } else {
            if (checkAnchor && accountType == AccountConstant.ACCOUNT_TYPE_MASTER) {
                // 做主持人登录校验 如果没有主持人数据，提示用户先添加主持人
                throw new BusinessException(BizErrorCode.BIZ_INVALID_PHONE);
            }
            // 获取游客cookie信息
            String visitorCookieStr = getVisitorCookieInfo(request);
            // 游客登录信息更新
            if (!visitorCookieStr.isEmpty()) {
                JSONObject visitorCookieData = JSON.parseObject(visitorCookieStr);
                String visitorPhone = visitorCookieData.getString(PHONE);
                String visitorNickName = visitorCookieData.getString(NICKNAME);
                wrapper = new LambdaQueryWrapper<AccountEntity>().eq(AccountEntity::getPhone, visitorPhone)
                        .eq(AccountEntity::getNickname, visitorNickName).last(LIMIT_1);
                accountEntity = accountMapper.selectOne(wrapper);
                if (accountEntity != null) {
                    accountEntity.setToken(token);
                    accountEntity.setPhone(Long.valueOf(phone));
                    accountEntity.setUsername(name);
                    accountEntity.setNickname(nickname);
                    accountEntity.setAccountType(accountType);
                    accountEntity.setUpdatedAt(LocalDateTime.now());
                    accountMapper.updateById(accountEntity);
                    // 清空游客cookie信息
                    Cookie cookie = new Cookie(VISITOR, "");
                    response.addCookie(cookie);
                }
            }
            if (accountEntity == null) {
                // 创建用户信息（用户和游客信息，都不存在时新建）
                accountEntity = new AccountEntity(Long.valueOf(phone), name, nickname, token, accountType);
                accountEntity.setDeviceId(deviceId);
                accountEntity.setNickname(nickname);
                accountEntity.setLogintokenEncrypted(authLoginVO.getLogintokenEncrypted());
                accountMapper.insert(accountEntity);
            }
        }

        AccountDTO accountDTO = new AccountDTO();
        BeanUtils.copyProperties(accountEntity, accountDTO);

        //保持原有代码  助理 和嘉宾 数据库里面存的类型是 4和5  给前端返回的类型 依旧是2  观众
        if (accountEntity.getAccountType().intValue() == AccountConstant.ACCOUNT_TYPE_ASSIST.intValue()
                || accountEntity.getAccountType().intValue() == AccountConstant.ACCOUNT_TYPE_GUEST.intValue()) {
            accountDTO.setAccountType(AccountConstant.ACCOUNT_TYPE_WATCH);
        }
        return accountDTO;
    }

    @Override
    public void authLogout(AuthLogoutVO authLogoutVO) {
        String token = TokenThreadLocal.getToken().getToken();
        redisTemplate.delete(RedisKey.AUTH_TOKEN + token);
    }

    @Override
    public ThirdAuthLoginRspVO thirdAuthLogin(ThirdAuthLoginVO authLoginVO, HttpServletRequest request,
                                              HttpServletResponse response) throws BusinessException {
        ThirdAuthLoginRspVO thirdAuthLoginRspVO = new ThirdAuthLoginRspVO();
        // 查询三方用户信息
        String thirdUserId = authLoginVO.getThirdUserId();
        LambdaQueryWrapper<AccountEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(AccountEntity::getThirdUserId, thirdUserId).last(LIMIT_1);
        AccountEntity accountEntity = accountMapper.selectOne(queryWrapper);
        if (accountEntity != null) {
            if (accountEntity.getStatus().equals(AccountConstant.STATUS_DISABLED)) {
                throw new BusinessException(BizErrorCode.AUTH_LOGIN_ACCOUNT_DISABLE);
            }
            Object tokenData = redisTemplate.opsForValue().get(RedisKey.AUTH_TOKEN + accountEntity.getToken());
            boolean needUpdate = false;
            if (tokenData != null) {
                JSONObject jsonObject = JSON.parseObject(tokenData.toString());
                thirdAuthLoginRspVO = JSON.parseObject(jsonObject.toString(), ThirdAuthLoginRspVO.class);
            } else {
                //生成token
                String token = createLoginToken(thirdUserId);
                accountEntity.setToken(token);
                needUpdate = true;
            }
            Long oldPhone = thirdAuthLoginRspVO.getPhone();
            String oldPhoneStr = oldPhone != null ? oldPhone.toString() : "";
            boolean update = !authLoginVO.getNickname().equals(thirdAuthLoginRspVO.getNickname())
                    || !authLoginVO.getUsername().equals(thirdAuthLoginRspVO.getUsername())
                    || (authLoginVO.getProfilePhoto() != null && !authLoginVO.getProfilePhoto()
                    .equals(thirdAuthLoginRspVO.getProfilePhoto()))
                    || (authLoginVO.getPhone() != null && !authLoginVO.getPhone().equals(oldPhoneStr))
                    || needUpdate;
            if (update) {
                // 更新用户表
                BeanUtils.copyProperties(authLoginVO, accountEntity);
                if (authLoginVO.getPhone() != null) {
                    accountEntity.setPhone(Long.valueOf(authLoginVO.getPhone()));
                }
                accountMapper.updateById(accountEntity);
                // 刷新redis数据
                setCacheData(thirdAuthLoginRspVO, authLoginVO);
            }
        } else {
            // 创建账号
            accountEntity = new AccountEntity();
            BeanUtils.copyProperties(authLoginVO, accountEntity);
            String token = createLoginToken(thirdUserId);
            accountEntity.setToken(token);
            if (authLoginVO.getPhone() != null) {
                accountEntity.setPhone(Long.valueOf(authLoginVO.getPhone()));
            }
            accountEntity.setAccountType(AccountConstant.ACCOUNT_TYPE_WATCH);
            accountMapper.insert(accountEntity);
            // 刷新redis数据
            setCacheData(thirdAuthLoginRspVO, authLoginVO);
        }
        return thirdAuthLoginRspVO;
    }

    /**
     * 游客登录
     *
     * @param authLoginVO 请求参数
     * @param request     请求数据
     * @param response    响应数据
     * @return
     * @throws BusinessException
     */
    @Override
    public VisitorAuthLoginRspVO visitor(VisitorAuthLoginVO authLoginVO, HttpServletRequest request, HttpServletResponse response) throws BusinessException {
        //先尝试从cookie中获取游客信息
        String visitorCookieStr = getVisitorCookieInfo(request);
        String phone;
        String nickName;
        if (!visitorCookieStr.isEmpty()) {
            // 已有游客信息
            JSONObject visitorCookieData = JSON.parseObject(visitorCookieStr);
            phone = visitorCookieData.getString(PHONE);
            nickName = visitorCookieData.getString(NICKNAME);
        } else {
            // 生成游客信息
            String phoneRandom = String.valueOf(System.currentTimeMillis()) + RandomUtil.getRandomNum(1000, 9999);
            phone = StringUtils.reverse(phoneRandom);
            nickName = "visitor_" + RandomUtil.getRandomNum(1000000, 9999999);
        }

        // 游客登录
        AuthLoginVO loginVO = new AuthLoginVO(phone, nickName, AccountConstant.ACCOUNT_TYPE_VISITOR, defaultAvatar);
        AuthLoginRspVO loginRspVO = authLogin(loginVO, request, response, false);

        if (AccountConstant.STATUS_DISABLED.equals(loginRspVO.getStatus())) {
            throw new BusinessException(BizErrorCode.AUTH_LOGIN_ACCOUNT_DISABLE);
        }

        // 存储redis
        String cacheValue = JSON.toJSONString(loginRspVO);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + loginRspVO.getToken(), cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);

        // 设置游客cookie信息
        JSONObject cookieData = new JSONObject();
        cookieData.put(PHONE, phone);
        cookieData.put(NICKNAME, nickName);
        try {
            String encodeCookie = java.net.URLEncoder.encode(cookieData.toJSONString(), CommonConstant.UTF8);
            Cookie cookie = new Cookie(VISITOR, encodeCookie);
            response.addCookie(cookie);
        } catch (Exception e) {
            log.error("cookie信息处理错误, {}", cookieData.toJSONString());
        }

        // 返回数据格式化
        VisitorAuthLoginRspVO visitorAuthLoginRspVO = new VisitorAuthLoginRspVO();
        BeanUtils.copyProperties(loginRspVO, visitorAuthLoginRspVO);
        visitorAuthLoginRspVO.setChooseNickname(visitorAuthLoginRspVO.getNickname().contains("visitor_") ? 0 : 1);
        return visitorAuthLoginRspVO;
    }

    /**
     * 处理缓存信息 & 返回数据
     *
     * @param thirdAuthLoginRspVO 返回数据
     * @param authLoginVO         登录参数
     */
    private void setCacheData(ThirdAuthLoginRspVO thirdAuthLoginRspVO, ThirdAuthLoginVO authLoginVO) {
        // 查询db数据
        String thirdUserId = authLoginVO.getThirdUserId();
        LambdaQueryWrapper<AccountEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(AccountEntity::getThirdUserId, thirdUserId).last(LIMIT_1);
        AccountEntity accountEntity = accountMapper.selectOne(queryWrapper);

        // 刷新redis
        BeanUtils.copyProperties(accountEntity, thirdAuthLoginRspVO);
        thirdAuthLoginRspVO.setAppId(paasService.getAppId());
        thirdAuthLoginRspVO.setModules(Arrays.asList(AccountConstant.ALLOW_MODULES));
        thirdAuthLoginRspVO.setThirdPartyUserId(accountEntity.getAccountId());

        // 电话号码处理
        Long phone = accountEntity.getPhone();
        if (phone == null) {
            thirdAuthLoginRspVO.setPhone(0L);
        }
        // 性别处理
        thirdAuthLoginRspVO.setSexStr(getSexStr(accountEntity.getSex()));
        // 状态处理
        thirdAuthLoginRspVO.setStatusStr(getStatusStr(accountEntity.getStatus()));
        // 账号类型处理
        thirdAuthLoginRspVO.setTypeStr(getAccountTypeStr(accountEntity.getAccountType()));
        String cacheValue = JSON.toJSONString(thirdAuthLoginRspVO);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + accountEntity.getToken(), cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);
    }

    /**
     * 获取性别字符串
     *
     * @param sex 性别
     * @return
     */
    private String getSexStr(Integer sex) {
        String sexStr = "未知";
        if (AccountConstant.SEX_MEN.equals(sex)) {
            sexStr = "男";
        } else if (AccountConstant.SEX_WOMEN.equals(sex)) {
            sexStr = "女";
        }
        return sexStr;
    }

    /**
     * 状态字符串
     *
     * @param status
     * @return
     */
    private String getStatusStr(Integer status) {
        String statusStr = "未知";
        if (AccountConstant.STATUS_DISABLED.equals(status)) {
            statusStr = "封停";
        } else if (AccountConstant.STATUS_ENABLED.equals(status)) {
            statusStr = "正常";
        }
        return statusStr;
    }

    /**
     * 获取账号类型字符串
     *
     * @param accountType
     * @return
     */
    private String getAccountTypeStr(Integer accountType) {
        String typeStr = "未知";
        if (AccountConstant.TYPE_WATCH.equals(accountType)) {
            typeStr = "观众";
        } else if (AccountConstant.TYPE_MASTER.equals(accountType)) {
            typeStr = "主持人";
        } else if (AccountConstant.TYPE_INTERACTION.equals(accountType)) {
            typeStr = "嘉宾/互动者";
        } else if (AccountConstant.TYPE_ASSISTANT.equals(accountType)) {
            typeStr = "助理";
        }
        return typeStr;
    }

    /**
     * 生成token
     *
     * @param keyword 标记
     * @return
     */
    private String createLoginToken(String keyword) {
        // 生成token
        String tokenString = RandomUtil.getRandomNum(1000, 9999) + keyword + RandomUtil.getRandomNum(1000, 9999);
        return MD5.create().digestHex16(tokenString);
    }

    /**
     * 获取游客cookie信息
     *
     * @param request
     * @return
     */
    private String getVisitorCookieInfo(HttpServletRequest request) {
        // get cookie data
        String visitorCookieStr = "";
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (VISITOR.equals(cookie.getName())) {
                    try {
                        visitorCookieStr = java.net.URLDecoder.decode(cookie.getValue(), CommonConstant.UTF8);
                    } catch (Exception e) {
                        log.error("cookie信息处理错误, {}", cookie.toString());
                    }
                    break;
                }
            }
        }
        return visitorCookieStr;
    }

    /**
     * 查询ids中的是观众的人的accountid
     *
     * @param ids
     * @return
     */
    @Override
    public List<Integer> filterAccountType(List<Integer> ids) {
        LambdaQueryWrapper<AccountEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AccountEntity::getAccountType, AccountConstant.ACCOUNT_TYPE_WATCH);
        queryWrapper.in(AccountEntity::getAccountId, ids);
        List<AccountEntity> accountEntities = list(queryWrapper);
        return accountEntities.stream().map(AccountEntity::getAccountId).collect(Collectors.toList());
    }

    @Override
    public AdmAccountListRspVO list(AdmAccountListReqVO reqVO) {
        AdmAccountListRspVO rspVO = new AdmAccountListRspVO();
        LambdaQueryWrapper<AccountEntity> where = Wrappers.lambdaQuery();
        where.eq(reqVO.getType() != null, AccountEntity::getAccountType, reqVO.getType())
                .isNull(AccountEntity::getDeletedAt)
                .ge(reqVO.getBeginTime() != null, AccountEntity::getCreatedAt, reqVO.getBeginTime());
        if (reqVO.getEndTime() != null) {
            where.le(AccountEntity::getCreatedAt, reqVO.getEndTime());
        }
        if (StringUtils.isNotBlank(reqVO.getKeyword())) {
            where.and(w -> w.like(AccountEntity::getUsername, reqVO.getKeyword())
                    .or()
                    .like(AccountEntity::getPhone, reqVO.getKeyword())
                    .or()
                    .like(AccountEntity::getNickname, reqVO.getKeyword()));
        }
        Integer total = accountMapper.selectCount(where);
        rspVO.setTotal(total);
        where.orderByDesc(AccountEntity::getAccountId);
        int currPage = reqVO.getPage() < 0 ? 1 : reqVO.getPage();
        rspVO.setCurrentPage(currPage);
        int pageSize = 10;
        rspVO.setPerPage(pageSize);
        int offset = (currPage - 1) * pageSize;
        where.last("limit " + offset + "," + pageSize);
        List<AccountEntity> accountListByPage = accountMapper.selectList(where);
        List<AccountListVO> list = new LinkedList<>();
        for (AccountEntity single : accountListByPage) {
            AccountListVO alv = new AccountListVO();
            BeanUtils.copyProperties(single, alv);
            alv.setStatusStr(getStatusStr(single.getStatus()));
            alv.setSexStr(getSexStr(single.getSex()));
            alv.setTypeStr(getAccountTypeStr(single.getAccountType()));
            list.add(alv);
        }
        rspVO.setPath("/");
        Integer lastPage = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;
        rspVO.setLastPage(lastPage);
        Integer firstItem = offset + 1;
        rspVO.setFrom(firstItem);
        Integer lastItem = firstItem + total - 1;
        rspVO.setTo(lastItem);
        rspVO.setFirstPageUrl(getUrl(1));
        rspVO.setLastPageUrl(getUrl(lastPage));
        if (lastPage > currPage) {
            rspVO.setLastPageUrl(getUrl(currPage + 1));
        }
        if (currPage > 1) {
            rspVO.setPrevPageUrl(getUrl(currPage - 1));
        }
        rspVO.setRecords(list);
        return rspVO;
    }

    @Override
    public Boolean edit(AdmAccountEditReqVO vo) {
        AccountEntity accountInfo = accountMapper.selectById(vo.getAccountId());
        if (accountInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_USER);
        }
        if (!vo.getPhone().equals(String.valueOf(accountInfo.getPhone()))) {
            LambdaQueryWrapper<AccountEntity> where = Wrappers.lambdaQuery();
            where.eq(AccountEntity::getPhone, vo.getPhone());
            where.eq(AccountEntity::getAccountType, AccountConstant.ACCOUNT_TYPE_MASTER);
            Integer count = accountMapper.selectCount(where);
            if (0 != count) {
                throw new BusinessException(BizErrorCode.AUTH_PHONE_ALREADY_EXIST);
            }
        }
        AccountEntity param = new AccountEntity();
        param.setAccountId(vo.getAccountId());
        param.setPhone(Long.valueOf(vo.getPhone()));
        param.setUsername(vo.getUserName());
        param.setNickname(vo.getNickName());
        param.setUpdatedAt(LocalDateTime.now());
        int i = accountMapper.updateById(param);
        return i > 0;
    }

    @Override
    public Boolean editNickName(Integer accountId, AccountEditVO vo) {
        AccountEntity entity = accountMapper.selectById(accountId);
        if (entity == null) {
            throw new BusinessException(BizErrorCode.EMPTY_USER);
        }
        entity.setNickname(vo.getNickname());
        entity.setUpdatedAt(LocalDateTime.now());
        int i = accountMapper.updateById(entity);
        return i > 0;
    }

    @Override
    public Boolean editStatus(AdmAccountEditStatusReqVO vo) {
        AccountEntity accountInfo = accountMapper.selectById(vo.getAccountId());
        if (accountInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_USER);
        }
        AccountEntity param = new AccountEntity();
        param.setAccountId(vo.getAccountId());
        param.setStatus(vo.getStatus());
        param.setUpdatedAt(LocalDateTime.now());
        int i = accountMapper.updateById(param);
        String token = (String) redisTemplate.opsForValue().get(RedisKey.AUTH_TOKEN + accountInfo.getToken());
        if (StringUtils.isNotEmpty(token)) {
            Token existToken = JsonUtil.objectFromJSONString(token, Token.class);
            if (existToken.getAccountId().compareTo(vo.getAccountId()) == 0) {
                existToken.setStatus(vo.getStatus());
                redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + accountInfo.getToken(),
                        JSON.toJSONString(existToken), AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);
            }
        }
        return i > 0;
    }

    @Override
    public Integer countUserForAdminExport(Integer userType, AdminExportCommonQueryCondition condition) {
        QueryWrapper<Object> wrapper = new QueryWrapper<>();
        wrapper.eq("account_type", userType);
        if (Objects.nonNull(condition)) {
            String createdAtColumn = "created_at";
            // 开始日期、结束日期
            wrapper.ge(Objects.nonNull(condition.getStartTime()), createdAtColumn, condition.getStartTime());
            if (Objects.nonNull(condition.getEndTime())) {
                wrapper.lt(createdAtColumn, condition.getEndTime().plusDays(1));
            }
            String keyword = condition.getKeyword();
            // 模糊查询支持，账号、手机号、昵称
            wrapper.and(StringUtils.isNotEmpty(keyword),
                    w -> w.like("username", keyword).or().like(PHONE, keyword).or().like(NICKNAME, keyword));
        }
        return accountMapper.countAccountForAdmin(wrapper);
    }

    @Override
    public JSONObject permission(Integer accountId, HttpServletRequest request, HttpServletResponse response) {
        JSONObject resp = new JSONObject();
        resp.put("account_id", accountId);

        // 此处预留接口，当控制台需要和定制业务权限系统打通时，通过权限系统获取权限
        // 数据结构参见接口 http://chandao.ops.vhall.com:3000/project/3061/interface/api/108778
        String permission = "[ { 'name': 'room-manage', 'title': '房间管理', 'show': 'true' }, { 'name': 'data-count-list', 'title': '数据统计', 'show': 'true' }, { 'name': 'video.group', 'title': '媒资管理', 'show': 'true', 'children': [ { 'name': 'VideoList', 'title': '音视频管理', 'show': 'true' }, { 'name': 'console-tailoring', 'title': '视频裁剪', 'show': 'true' } ] }, { 'name': 'resource.group', 'title': '资源管理', 'show': 'true', 'children': [ { 'name': 'doc-management', 'title': '文档管理', 'show': 'true' }, { 'name': 'survey-list', 'title': '问卷管理', 'show': 'true' }, { 'name': 'vote-list', 'title': '投票管理', 'show': 'true' }, { 'name': 'exam-list', 'title': '考试管理', 'show': 'true' }, { 'name': 'subject', 'title': '专题管理', 'show': 'true' }, { 'name': 'commodity-management', 'title': '商品管理', 'show': 'true' }, { 'name': 'sensitive-words-management', 'title': '敏感词管理', 'show': 'true' }, { 'name': 'pendant-management', 'title': '挂件管理', 'show': 'true' }, { 'name': 'gift', 'title': '表情管理', 'show': 'false' }, { 'name': 'anchor-manage', 'title': '主播管理', 'show': 'false' } ] } ]";
        resp.put("permission", JSON.parse(permission));

        return resp;
    }

    @Override
    public List<NicknamesVO> getRandomNicknames() {

        List<NicknamesEntity> nicknamesEntities = nicknamesMapper.selectListRandom();
        List<NicknamesVO> nicknamesVOS = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(nicknamesEntities)) {
            nicknamesVOS = BeanCopyUtil.batchCopy(nicknamesEntities, NicknamesVO::new);
        }
        return nicknamesVOS;
    }

    @Override
    public boolean chooseNickname(ChooseNicknameVO chooseNicknameVO) {

        if (chooseNicknameVO.getNickNameId() == null && StringUtils.isBlank(chooseNicknameVO.getNickName())) {
            throw new BusinessException("参数不能为空！");
        }

        String nickName;
        if (chooseNicknameVO.getNickNameId() == null && StringUtils.isNotBlank(chooseNicknameVO.getNickName())) {
            // 校验昵称是库里面的
            LambdaQueryWrapper<NicknamesEntity> where = Wrappers.lambdaQuery();
            where.eq(NicknamesEntity::getUsed, 0);
            where.eq(NicknamesEntity::getNickname, chooseNicknameVO.getNickName());
            NicknamesEntity nicknamesEntity = nicknamesMapper.selectOne(where);
            Preconditions.checkArgument(nicknamesEntity == null, "当前昵称已经存在！");

            nicknamesEntity = new NicknamesEntity();
            nicknamesEntity.setUsed(1);
            nicknamesEntity.setNickname(chooseNicknameVO.getNickName());
            nicknamesMapper.insert(nicknamesEntity);
            nickName = chooseNicknameVO.getNickName();
        } else {
            // 校验昵称是库里面的
            LambdaQueryWrapper<NicknamesEntity> where = Wrappers.lambdaQuery();
            where.eq(NicknamesEntity::getUsed, 0);
            where.eq(NicknamesEntity::getId, chooseNicknameVO.getNickNameId());
            NicknamesEntity nicknamesEntity = nicknamesMapper.selectOne(where);
            Preconditions.checkArgument(nicknamesEntity != null, "当前昵称有误，请核对！");

            nicknamesEntity.setUsed(1);
            nicknamesEntity.setUpdatedAt(LocalDateTime.now());
            nicknamesMapper.updateById(nicknamesEntity);

            nickName = nicknamesEntity.getNickname();
        }


        VssToken vssToken = VssTokenThreadLocal.getToken();
        AccountEntity accountEntity = accountMapper.selectById(vssToken.getThirdPartyUserId());
        Preconditions.checkArgument(accountEntity != null, "当前人员登录有误，请核对！");
        accountEntity.setNickname(nickName);
        accountMapper.updateById(accountEntity);

        // 房间号不为空，给这个房间发个消息，刷新人员列表，显示昵称
        if (StringUtils.isNotBlank(chooseNicknameVO.getRoomId())) {

            ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("send-message-%d").build();

            ScheduledThreadPoolExecutor executor1 = new ScheduledThreadPoolExecutor(1, namedThreadFactory);
            executor1.schedule(() -> {

                RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(chooseNicknameVO.getRoomId());

                HashMap<String, Object> map = Maps.newHashMap();
                map.put("type", SendMessageTypeEnum.CHOOSE_NICKNAME.getType());
                map.put("room_id", chooseNicknameVO.getRoomId());
                paasService.sendMessage(roomInfo.getChannelId(), JsonUtil.toJsonString(map));

            }, 2, TimeUnit.SECONDS);
            executor1.shutdown();


        }


        return true;
    }

    @Override
    public VisitorAuthLoginRspVO visitorUseDeviceId(VisitorAuthLoginVO authLoginVO, HttpServletRequest request, HttpServletResponse response) {

        Preconditions.checkArgument(StringUtils.isNotBlank(authLoginVO.getDeviceId()), "设备id不能为空！");

        log.info(MessageFormat.format("visitorUseDeviceId：【{0}】", JSON.toJSONString(authLoginVO)));

        LambdaQueryWrapper<AccountEntity> where = Wrappers.lambdaQuery();
        where.eq(AccountEntity::getDeviceId, authLoginVO.getDeviceId());
        where.eq(AccountEntity::getAccountType, AccountConstant.ACCOUNT_TYPE_VISITOR);
        where.eq(AccountEntity::getIsDeleted, DeleteStatusEnum.NO_DELETE.getStatus());
        List<AccountEntity> accountEntities = accountMapper.selectList(where);

        if (CollectionUtil.isNotEmpty(accountEntities) && accountEntities.size() > 1) {
            throw new BusinessException("当前设备绑定多个账号！");
        }

        String phone;
        String nickName;
        if (CollectionUtil.isNotEmpty(accountEntities) && accountEntities.size() == 1) {
            AccountEntity accountEntity = accountEntities.get(0);
            // 已有游客信息
            phone = accountEntity.getPhone() == null ? "" : String.valueOf(accountEntity.getPhone());
            nickName = accountEntity.getNickname();
        } else {
            // 生成游客信息
            String phoneRandom = String.valueOf(System.currentTimeMillis()) + RandomUtil.getRandomNum(1000, 9999);
            phone = StringUtils.reverse(phoneRandom);
            nickName = "visitor_" + RandomUtil.getRandomNum(1000000, 9999999);
        }

        // 游客登录
        AuthLoginVO loginVO = new AuthLoginVO(phone, nickName, AccountConstant.ACCOUNT_TYPE_VISITOR, defaultAvatar);
        loginVO.setDeviceId(authLoginVO.getDeviceId());

        AuthLoginRspVO loginRspVO = authLogin(loginVO, request, response, false);

        if (AccountConstant.STATUS_DISABLED.equals(loginRspVO.getStatus())) {
            throw new BusinessException(BizErrorCode.AUTH_LOGIN_ACCOUNT_DISABLE);
        }

        // 存储redis
        String cacheValue = JSON.toJSONString(loginRspVO);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + loginRspVO.getToken(), cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);

        // 设置游客cookie信息
        JSONObject cookieData = new JSONObject();
        cookieData.put(PHONE, phone);
        cookieData.put(NICKNAME, nickName);
        try {
            String encodeCookie = java.net.URLEncoder.encode(cookieData.toJSONString(), CommonConstant.UTF8);
            Cookie cookie = new Cookie(VISITOR, encodeCookie);
            response.addCookie(cookie);
        } catch (Exception e) {
            log.error("cookie信息处理错误, {}", cookieData.toJSONString());
        }

        // 返回数据格式化
        VisitorAuthLoginRspVO visitorAuthLoginRspVO = new VisitorAuthLoginRspVO();
        BeanUtils.copyProperties(loginRspVO, visitorAuthLoginRspVO);
        visitorAuthLoginRspVO.setChooseNickname(visitorAuthLoginRspVO.getNickname().contains("visitor_") ? 0 : 1);
        return visitorAuthLoginRspVO;
    }

    @Override
    public TouristJudgmentRspVO touristJudgment(TouristJudgmentVO touristJudgmentVO) {


        // 传入的是token
        String token = touristJudgmentVO.getToken();

        String tokenTemp = redisTemplateStr.opsForValue().get(RedisKey.AUTH_TOKEN + token);
        if (StringUtils.isNotBlank(tokenTemp)) {
            String string = JSON.parse(tokenTemp).toString();
            Token token1 = JSONObject.parseObject(string, Token.class);
            if (token1 != null && token1.getAccountId() != null) {
                AccountEntity accountEntity = accountMapper.selectById(token1.getAccountId());
                if (accountEntity != null && StringUtils.isNotBlank(accountEntity.getLogintokenEncrypted())) {
                    return TouristJudgmentRspVO.builder().type("console_user_login").description("已经登录console用户").build();
                }
            }

        }
        return TouristJudgmentRspVO.builder().type("tourist").description("游客").build();

    }
    @Override
    public ChangeAccountRoleResponseVO changeAccountRole(ChangeAccountRoleRequestVO vo) {
        Token token = TokenThreadLocal.getToken();
        Integer accountId = token.getAccountId();
        Integer roleId = vo.getRoleId();
        List<MultipleRoleVO> roleList = Optional.ofNullable(token.getRoleList()).orElseThrow(()->new BusinessException(BizErrorCode.BIZ_ROLE_NOT_MATCH));
        Set<Integer> roleIdSet = roleList.stream().map(MultipleRoleVO::getRoleId).collect(Collectors.toSet());
        if (CollUtil.isEmpty(roleIdSet) || !roleIdSet.contains(roleId)) {
            throw new BusinessException(BizErrorCode.BIZ_ROLE_NOT_MATCH);
        }

        Optional<MultipleRoleVO> first = roleList.stream().filter(data -> Objects.equals(data.getRoleId(), roleId)).findFirst();
        MultipleRoleVO multipleRoleVO = first.orElse(new MultipleRoleVO());

        changeAccountDBRole(accountId, roleId);
        changeTokenRole(token, roleId, multipleRoleVO);
        ChangeAccountRoleResponseVO result = new ChangeAccountRoleResponseVO();
        List<RolePermissionConsoleResponseVO> rolePermissionConsoleResponseVOS = getRolePermissionConsoleResponseVOS(roleId);
        result.setRoleId(roleId);
        result.setRoleType(multipleRoleVO.getRoleType());
        result.setRolePermissionConsoleResponseVOS(rolePermissionConsoleResponseVOS);
        return result;
    }

    /**
     * 改变用户数据库角色
     * @param accountId
     * @param roleId
     */
    private void changeAccountDBRole(Integer accountId, Integer roleId) {
        LambdaQueryWrapper<AccountEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(AccountEntity::getAccountType, AccountConstant.ACCOUNT_TYPE_MASTER)
                .eq(AccountEntity::getAccountId, accountId)
                .last(" limit 1 ");
        AccountEntity accountEntity = Optional.ofNullable(accountMapper.selectOne(queryWrapper)).orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_ACCOUNT));
        accountEntity.setRoleId(roleId);
        accountMapper.updateById(accountEntity);
    }

    /**
     * 改变token 角色
     * @param token
     * @param roleId
     * @param multipleRoleVO
     */
    private void changeTokenRole(Token token, Integer roleId, MultipleRoleVO multipleRoleVO) {
        token.setRoleId(roleId);
        if (Objects.nonNull(multipleRoleVO.getRoleType())) {
            token.setRoleType(multipleRoleVO.getRoleType());
        }
        String cacheValue = JSON.toJSONString(token);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + token.getToken(),
                cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);
    }

    @Override
    public AuthLoginRspVO loginSso(AuthConsoleLoginSsoVO vo) {
//        使用 code 获取外接用户信息
        JSONObject userData = ssoService.getThirdUserInfo(vo.getCode());
        String notesId = userData.getString("notesid");
//            用户权限组
        JSONArray role = userData.getJSONArray("role");
//        使用外接信息校验用户的role 是否有效
        List<RoleEntity> userRoleList = getUserRole(role);
        RoleEntity roleEntity = userRoleList.get(0);
        Integer roleId = roleEntity.getRoleId();
//        使用外接信息判定是否有用户
        String orgCode = userData.getString("orgcode");
        String orgName = userData.getString("branchname");
//        根据 sso 获取用户信息创建或者更新用户账户信息
        AccountEntity accountEntity = createOrUpdateAccount(userData, notesId, roleId);
//        创建组织架构
        addUserOrgInfo(orgCode, orgName);
        String token = createLoginToken(notesId);
        commonUpdateAccountLoginInfo(accountEntity,token);
        List<MultipleRoleVO> roleList = new ArrayList<>();
        for (RoleEntity rr : userRoleList){
            MultipleRoleVO multipleRoleVO = new MultipleRoleVO();
            multipleRoleVO.setDesc(rr.getDesc());
            multipleRoleVO.setName(rr.getName());
            multipleRoleVO.setRoleId(rr.getRoleId());
            multipleRoleVO.setRoleType(rr.getType());
            roleList.add(multipleRoleVO);
        }
        AuthLoginRspVO authLoginRspVO = buildAuthLoginInfoSetUserCache(roleEntity, orgCode, orgName, accountEntity, token, roleList);
        authLoginRspVO.setRoleId(roleId);
        authLoginRspVO.setRoleList(roleList);
        return authLoginRspVO;
    }

    @NotNull
    private AuthLoginRspVO buildAuthLoginInfoSetUserCache(RoleEntity userRole, String orgCode, String orgName, AccountEntity accountEntity, String token, List<MultipleRoleVO> roleList) {
        AccountDTO accountDTO = new AccountDTO();
        accountDTO.setToken(token);
        accountDTO.setAccountType(AccountConstant.ACCOUNT_TYPE);
        accountDTO.setThirdPartyUserId(accountEntity.getThirdUserId());
        Boolean headquartersMarkerFlag = getHeadquartersMarkerFlag(orgCode, orgName);
        accountDTO.setHeadquartersMarkerFlag(headquartersMarkerFlag);
        BeanUtils.copyProperties(accountEntity, accountDTO);
        accountDTO.setRoleType(userRole.getType());
        accountDTO.setRoleName(RoleNameEnum.HOST.getValue());
        accountDTO.setRoleList(roleList);
        return buildAuthLoginInfoSetUserCache(accountDTO);
    }

    @NotNull
    private AccountEntity createOrUpdateAccount(JSONObject userData, String notesId, Integer roleId) {
        LambdaQueryWrapper<AccountEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(AccountEntity::getAccountType,AccountConstant.ACCOUNT_TYPE_MASTER)
                .eq(AccountEntity::getThirdUserId, notesId)
                .last(" limit 1 ");
        AccountEntity accountEntity = accountMapper.selectOne(queryWrapper);
        String name = userData.getString("name");
        String sex = userData.getString("sex");
        String mobile = userData.getString("mobile");
//            所属组织架构code
        String orgCode = userData.getString("orgcode");
        String orgName = userData.getString("branchname");
        if (Objects.isNull(accountEntity)) {
//        无用户数据则进行创建用户,创建用户角色关联
            accountEntity = new AccountEntity();
            accountEntity.setThirdUserId(notesId);
            accountEntity.setAccountType(AccountConstant.ACCOUNT_TYPE_MASTER);
            accountEntity.setNickname(name);
            accountEntity.setUsername(name);
            accountEntity.setSex("男".equals(sex)?1:0);
            if (NumberUtil.isNumber(mobile)) {
                accountEntity.setPhone(Long.parseLong(mobile));
            }
            accountEntity.setRoleId(roleId);
            accountEntity.setOrgCode(orgCode);
            accountEntity.setOrgName(orgName);
            accountMapper.insert(accountEntity);
        }else{
            accountEntity.setThirdUserId(notesId);
            accountEntity.setNickname(name);
            accountEntity.setUsername(name);
            accountEntity.setSex("男".equals(sex)?1:0);
            if (NumberUtil.isNumber(mobile)) {
                accountEntity.setPhone(Long.parseLong(mobile));
            }
            accountEntity.setRoleId(roleId);
            accountEntity.setOrgCode(orgCode);
            accountEntity.setOrgName(orgName);
            accountMapper.updateById(accountEntity);
        }
        return accountEntity;
    }

    /**
     * 当组织架构没有的时候需要创建新的组织架构
     * @param orgCode
     * @param orgName
     */
    private void addUserOrgInfo(String orgCode, String orgName) {
        OrgEntity orgEntity = getOrgEntity(orgCode);
        if (Objects.isNull(orgEntity)) {
            String lockKey = RedisKey.CREATE_ORG_LOCK_KEY + orgCode;
            if (RedisLocker.tryLock(lockKey,orgCode,1000)) {
                orgEntity = getOrgEntity(orgCode);
                if (Objects.isNull(orgEntity)) {
                    orgEntity = new OrgEntity();
                    orgEntity.setOrgCode(orgCode);
                    orgEntity.setOrgName(orgName);
                    orgMapper.insert(orgEntity);
                }
            }
            RedisLocker.unlock(lockKey, orgCode);
        }
    }

    private OrgEntity getOrgEntity(String orgCode) {
        LambdaQueryWrapper<OrgEntity> orgWhere = Wrappers.lambdaQuery();
        orgWhere.eq(OrgEntity::getOrgCode, orgCode).last(" limit 1 ");
        return orgMapper.selectOne(orgWhere);
    }

    @Override
    public List<RolePermissionConsoleResponseVO> rolePermissionConsole(RolePermissionConsoleRequestVO requestVO) {
        Token token = TokenThreadLocal.getToken();
        Integer roleId = token.getRoleId();
        return getRolePermissionConsoleResponseVOS(roleId);
    }

    /**
     * 获取角色对应的用户角色菜单
     * @param roleId
     * @return
     */
    @Nullable
    private List<RolePermissionConsoleResponseVO> getRolePermissionConsoleResponseVOS(Integer roleId) {
        List<RolePermissionConsoleResponseVO> roleMenus = null;
        String key = RedisKey.ROLE_PERMISSION_MENUS+ roleId;
        if (!redisTemplate.hasKey(key)) {
            LambdaQueryWrapper<RoleMenuesEntity> where = Wrappers.lambdaQuery();
            where.eq(RoleMenuesEntity::getRoleId, roleId)
            .isNull(RoleMenuesEntity::getDeletedAt);
            List<RoleMenuesEntity> roleMenuesEntities = Optional.ofNullable(rbacRoleMenuesMapper.selectList(where))
                    .orElseThrow(()->new BusinessException(BizErrorCode.AUTH_ACCESS_MENUS_EMPTY));
            List<Integer> menuIds = roleMenuesEntities.stream().map(RoleMenuesEntity::getMenuId).collect(Collectors.toList());
            LambdaQueryWrapper<MenuesEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.isNull(MenuesEntity::getDeletedAt)
            .in(MenuesEntity::getMenuId,menuIds).orderByAsc(MenuesEntity::getSort);
            List<MenuesEntity> menuesEntities = Optional.ofNullable(menuMapper.selectList(queryWrapper))
                    .orElseThrow(()->new BusinessException(BizErrorCode.AUTH_ACCESS_MENUS_EMPTY));
            roleMenus = buildRolePermissionMenus(menuesEntities);
            redisTemplate.opsForValue().set(key,roleMenus,10,TimeUnit.MINUTES);
        }else{
            roleMenus = (List<RolePermissionConsoleResponseVO>) redisTemplate.opsForValue().get(key);
        }
//        获取用户 roleId
        return roleMenus;
    }

    @Override
    public List<AccountEntity> getAccountIntoList(List<Integer> accountIds) {
        List<AccountEntity> accountEntityList = new ArrayList<>();
        if (CollUtil.isNotEmpty(accountIds)) {
            LambdaQueryWrapper<AccountEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.in(AccountEntity::getAccountId, accountIds);
            accountEntityList = accountMapper.selectList(queryWrapper);
        }
        return accountEntityList;
    }



    /**
     * 根据菜单构建出菜单上下级结构返回结果
     * @param menuesEntities
     * @return List<RolePermissionConsoleResponseVO>
     */
    @NotNull
    private List<RolePermissionConsoleResponseVO> buildRolePermissionMenus(List<MenuesEntity> menuesEntities) {
        Map<Integer, List<MenuesEntity>> pidMenusMap = menuesEntities.stream().collect(Collectors.groupingBy(MenuesEntity::getPid));
        List<RolePermissionConsoleResponseVO> roleMenus = new ArrayList<>();
//        根目录菜单
        List<MenuesEntity> rootMenus = pidMenusMap.get(0);
        RolePermissionConsoleResponseVO rolePermissionConsoleResponseVO ;
        for (MenuesEntity rootMenu : rootMenus) {
            rolePermissionConsoleResponseVO = new RolePermissionConsoleResponseVO();
            Integer menuId = rootMenu.getMenuId();
            String url = rootMenu.getUrl();
            String name = rootMenu.getName();
            rolePermissionConsoleResponseVO.setMenuId(menuId);
            rolePermissionConsoleResponseVO.setName(url);
            rolePermissionConsoleResponseVO.setTitle(name);
            List<RolePermissionConsoleResponseVO> children ;
            List<MenuesEntity> menuesEntities1 = pidMenusMap.get(menuId);
            if (CollUtil.isNotEmpty(menuesEntities1)) {
                children = menuesEntities1.stream().map(menuesEntity->{
                    RolePermissionConsoleResponseVO temp = new RolePermissionConsoleResponseVO();
                    temp.setMenuId(menuesEntity.getMenuId());
                    temp.setName(menuesEntity.getUrl());
                    temp.setTitle(menuesEntity.getName());
                    return temp;
                }).collect(Collectors.toList());
                rolePermissionConsoleResponseVO.setChildren(children);
            }
            roleMenus.add(rolePermissionConsoleResponseVO);
        }
        return roleMenus;
    }

    /**
     * 更新用户 token
     * @param accountEntity
     * @param token
     */
    private void commonUpdateAccountLoginInfo(AccountEntity accountEntity, String token) {
        accountEntity.setToken(token);
        accountEntity.setUpdatedAt(LocalDateTime.now());
        accountMapper.updateById(accountEntity);
    }
    /**
     * 构建用户登录信息
     * @return AuthLoginRspVO
     */
    private AuthLoginRspVO buildAuthLoginInfoSetUserCache(AccountDTO accountDTO) {
// 设置用户缓存
        setUserCache(accountDTO);
        AuthLoginRspVO authLoginRspVO = new AuthLoginRspVO();
        BeanUtils.copyProperties(accountDTO, authLoginRspVO);
        authLoginRspVO.setAppId(paasService.getAppId());
        authLoginRspVO.setPhone(accountDTO.getPhone());
        authLoginRspVO.setSaveUserUrl("");
        return authLoginRspVO;
    }
    public void setUserCache(@javax.validation.constraints.NotNull AccountDTO accountDTO) {
        accountDTO.setAppId(paasService.getAppId());
        accountDTO.setModules(AccountConstant.ALLOW_MODULES);
        accountDTO.setThirdPartyUserId(accountDTO.getThirdPartyUserId());
        String cacheValue = JSON.toJSONString(accountDTO);
        redisTemplate.opsForValue().set(RedisKey.AUTH_TOKEN + accountDTO.getToken(),
                cacheValue, AccountConstant.TOKEN_TIME, TimeUnit.SECONDS);
    }

    /**
     * 获取用户角色
     * @param roles
     * @return
     */
    private List<RoleEntity> getUserRole(JSONArray roles) {
        int size = roles.size();
        if (size<=0) {
//            权限不存在
            throw new BusinessException(BizErrorCode.AUTH_ACCESS_EMPTY);
        }
        ArrayList<String> roleArr = new ArrayList<>();
        for (int i = 0; i < roles.size(); i++) {
            roleArr.add(roles.getString(i));
        }
        LambdaQueryWrapper<RoleEntity> where = Wrappers.lambdaQuery();
        where.in(RoleEntity::getName,roleArr);
        List<RoleEntity> roleEntities = roleMapper.selectList(where);
        if (CollUtil.isEmpty(roleEntities)) {
            throw new BusinessException(BizErrorCode.AUTH_ACCESS_EMPTY);
        }
        return roleEntities;
    }


    private String getUrl(Integer page) {
        if (page <= 0) {
            page = 1;
        }
        return "/?page=" + page;
    }

}